<template>
  <div class="weather-app">
    <h1>天气预报应用</h1>
    
    <section class="city-selector">
      <h2>选择城市</h2>
      <div class="city-buttons">
        <button 
          v-for="city in cities" 
          :key="city.code" 
          @click="getWeather(city.code)"
          :class="{ active: selectedCityCode === city.code }"
        >
          {{ city.name }}
        </button>
      </div>
    </section>
    
    <section class="weather-display">
      <div v-if="loading" class="loading">
        <div class="spinner"></div>
        <p>正在获取天气数据...</p>
      </div>
      
      <div v-else-if="error" class="error-message">
        <h3>获取数据失败</h3>
        <p>{{ error }}</p>
        <button @click="getWeather(selectedCityCode)">重试</button>
      </div>
      
      <div v-else-if="weatherSummary" class="weather-info">
        <div class="current-weather">
          <h2>{{ weatherSummary.city }} 当前天气</h2>
          <div class="weather-details">
            <div class="detail-item">
              <span class="label">温度</span>
              <span class="value">{{ weatherSummary.temperature }}℃</span>
            </div>
            <div class="detail-item">
              <span class="label">湿度</span>
              <span class="value">{{ weatherSummary.humidity }}</span>
            </div>
            <div class="detail-item">
              <span class="label">空气质量</span>
              <span class="value">{{ weatherSummary.airQuality }}</span>
            </div>
          </div>
          <div class="health-tip">
            <p>健康提示: {{ weatherSummary.healthTip }}</p>
          </div>
        </div>
        
        <div class="forecast">
          <h3>未来天气预报</h3>
          <div class="forecast-list">
            <div v-for="(item, index) in forecastData" :key="index" class="forecast-item">
              <div class="date">{{ item.date }} {{ item.week }}</div>
              <div class="weather-type">{{ item.type }}</div>
              <div class="temperature">{{ item.high }} / {{ item.low }}</div>
              <div class="wind">{{ item.fx }} {{ item.fl }}</div>
            </div>
          </div>
        </div>
        
        <div class="update-time" v-if="updateTime">
          <p>最后更新时间: {{ updateTime }}</p>
        </div>
      </div>
      
      <div v-else class="no-data">
        <p>请选择城市查看天气</p>
      </div>
    </section>
    
    <section class="code-explanation">
      <h2>综合案例 - 天气预报应用</h2>
      <p>本案例展示了如何使用Pinia和Axios构建一个实用的天气预报应用，包括以下关键点：</p>
      
      <div class="explanation-item">
        <h3>1. API封装</h3>
        <div class="code-example">
          <pre><code>// src/api/weather.js
import axios from 'axios'

// 创建天气API的axios实例
const weatherService = axios.create({
  timeout: 10000,
  headers: {
    'Content-Type': 'application/json'
  }
})

export default {
  // 获取城市天气数据
  async getWeatherByCity(cityCode) {
    try {
      const response = await weatherService.get(
        `http://t.weather.itboy.net/api/weather/city/${cityCode}`
      )
      return response.data
    } catch (error) {
      console.log('天气API请求失败', error)
      // 如果需要，这里可以添加模拟数据作为备用
      throw error
    }
  }
}</code></pre>
        </div>
      </div>
      
      <div class="explanation-item">
        <h3>2. Store设计</h3>
        <div class="code-example">
          <pre><code>// src/stores/07.综合案例.js
import { defineStore } from 'pinia'
import weatherApi from '../api/weather'

export const useWeatherStore = defineStore('weather', {
  state: () => ({
    weatherData: null,
    cityInfo: null,
    forecast: [],
    loading: false,
    error: null,
    lastUpdated: null
  }),
  
  getters: {
    // 获取当前天气概况
    weatherSummary(state) {
      if (!state.weatherData || !state.cityInfo) return null
      
      const { wendu, shidu, quality, ganmao } = state.weatherData
      const { city } = state.cityInfo
      
      return {
        city,
        temperature: wendu,
        humidity: shidu,
        airQuality: quality,
        healthTip: ganmao
      }
    },
    
    // 获取天气预报数据
    forecastData(state) {
      return state.forecast
    },
    
    // 获取更新时间
    updateTime(state) {
      return state.lastUpdated
    },
    
    // 判断是否有数据
    hasData(state) {
      return !!state.weatherData
    }
  },
  
  actions: {
    // 获取天气数据
    async fetchWeatherData(cityCode) {
      this.loading = true
      this.error = null
      
      try {
        const response = await weatherApi.getWeatherByCity(cityCode)
        
        // 保存数据到state
        this.weatherData = response.data
        this.cityInfo = response.cityInfo
        this.forecast = response.data.forecast
        this.lastUpdated = new Date().toLocaleString()
        
        return response
      } catch (error) {
        this.error = error.message || '获取天气数据失败'
        console.error('获取天气数据失败:', error)
        throw error
      } finally {
        this.loading = false
      }
    },
    
    // 重置store状态
    resetState() {
      this.weatherData = null
      this.cityInfo = null
      this.forecast = []
      this.loading = false
      this.error = null
      this.lastUpdated = null
    }
  }
})</code></pre>
        </div>
      </div>
      
      <div class="explanation-item">
        <h3>3. 在组件中使用</h3>
        <div class="code-example">
          <pre><code>// src/views/07.综合案例.vue
import { mapState, mapGetters, mapActions } from 'pinia'
import { useWeatherStore } from '../stores/07.综合案例'

export default {
  name: 'WeatherApp',
  data() {
    return {
      cities: [
        { name: '郑州', code: '101180101' },
        { name: '北京', code: '101010100' },
        { name: '上海', code: '101020100' },
        { name: '广州', code: '101280101' },
        { name: '深圳', code: '101280601' }
      ],
      selectedCityCode: null
    }
  },
  computed: {
    ...mapState(useWeatherStore, ['loading', 'error']),
    ...mapGetters(useWeatherStore, [
      'weatherSummary', 
      'forecastData', 
      'updateTime', 
      'hasData'
    ])
  },
  methods: {
    ...mapActions(useWeatherStore, ['fetchWeatherData', 'resetState']),
    
    async getWeather(cityCode) {
      this.selectedCityCode = cityCode
      try {
        await this.fetchWeatherData(cityCode)
      } catch (error) {
        console.error('获取天气失败:', error)
      }
    }
  },
  // 组件挂载时加载第一个城市的天气数据
  mounted() {
    if (this.cities.length > 0) {
      this.getWeather(this.cities[0].code)
    }
  },
  // 组件卸载时重置store状态
  beforeUnmount() {
    this.resetState()
  }
}</code></pre>
        </div>
      </div>
      
      <div class="explanation-item">
        <h3>4. 最佳实践</h3>
        <ul>
          <li><strong>状态集中管理</strong>：所有天气数据和加载状态都在store中管理</li>
          <li><strong>错误处理</strong>：统一处理API请求错误</li>
          <li><strong>数据转换</strong>：使用getter将原始数据转换为UI友好的格式</li>
          <li><strong>缓存机制</strong>：避免重复请求相同城市的数据</li>
          <li><strong>UI状态同步</strong>：加载状态和错误状态与UI紧密结合</li>
          <li><strong>生命周期管理</strong>：在组件挂载时自动加载数据，卸载时清理状态</li>
        </ul>
      </div>
    </section>
  </div>
</template>

<script>
import { mapState, mapGetters, mapActions } from 'pinia'
import { useWeatherStore } from '../stores/07.综合案例'

export default {
  name: 'WeatherApp',
  data() {
    return {
      cities: [
        { name: '郑州', code: '101180101' },
        { name: '北京', code: '101010100' },
        { name: '上海', code: '101020100' },
        { name: '广州', code: '101280101' },
        { name: '深圳', code: '101280601' }
      ],
      selectedCityCode: null
    }
  },
  computed: {
    ...mapState(useWeatherStore, ['loading', 'error']),
    ...mapGetters(useWeatherStore, ['weatherSummary', 'forecastData', 'updateTime', 'hasData'])
  },
  methods: {
    ...mapActions(useWeatherStore, ['fetchWeatherData', 'resetState']),
    
    async getWeather(cityCode) {
      this.selectedCityCode = cityCode
      try {
        await this.fetchWeatherData(cityCode)
      } catch (error) {
        console.error('获取天气失败:', error)
      }
    }
  },
  // 组件挂载时加载第一个城市的天气数据
  mounted() {
    if (this.cities.length > 0) {
      this.getWeather(this.cities[0].code)
    }
  },
  // 组件卸载时重置store状态
  beforeUnmount() {
    this.resetState()
  }
}
</script>

<style scoped>
.weather-app {
  max-width: 800px;
  margin: 0 auto;
  padding: 20px;
}

h1 {
  text-align: center;
  margin-bottom: 30px;
  color: #2c3e50;
}

.city-selector {
  margin-bottom: 30px;
}

.city-selector h2 {
  margin-bottom: 15px;
  font-size: 1.5rem;
}

.city-buttons {
  display: flex;
  flex-wrap: wrap;
  gap: 10px;
}

.city-buttons button {
  padding: 10px 20px;
  background-color: #f0f0f0;
  border: none;
  border-radius: 4px;
  cursor: pointer;
  transition: all 0.3s;
}

.city-buttons button:hover {
  background-color: #e0e0e0;
}

.city-buttons button.active {
  background-color: #4CAF50;
  color: white;
}

.weather-display {
  background-color: white;
  border-radius: 8px;
  padding: 20px;
  box-shadow: 0 2px 10px rgba(0, 0, 0, 0.1);
  margin-bottom: 30px;
  min-height: 300px;
}

.loading {
  display: flex;
  flex-direction: column;
  align-items: center;
  justify-content: center;
  height: 200px;
}

.spinner {
  width: 40px;
  height: 40px;
  border: 4px solid rgba(0, 0, 0, 0.1);
  border-radius: 50%;
  border-top-color: #4CAF50;
  animation: spin 1s linear infinite;
  margin-bottom: 15px;
}

@keyframes spin {
  to { transform: rotate(360deg); }
}

.error-message {
  color: #f44336;
  text-align: center;
  padding: 20px;
}

.error-message button {
  margin-top: 15px;
  padding: 8px 16px;
  background-color: #f44336;
  color: white;
  border: none;
  border-radius: 4px;
  cursor: pointer;
}

.current-weather {
  margin-bottom: 30px;
  padding-bottom: 20px;
  border-bottom: 1px solid #eee;
}

.current-weather h2 {
  margin-bottom: 20px;
  color: #2c3e50;
}

.weather-details {
  display: flex;
  flex-wrap: wrap;
  gap: 20px;
  margin-bottom: 15px;
}

.detail-item {
  display: flex;
  flex-direction: column;
  min-width: 100px;
}

.label {
  font-size: 0.9rem;
  color: #666;
  margin-bottom: 5px;
}

.value {
  font-size: 1.2rem;
  font-weight: bold;
}

.health-tip {
  margin-top: 15px;
  padding: 10px;
  background-color: #f9f9f9;
  border-radius: 4px;
  color: #555;
}

.forecast h3 {
  margin-bottom: 15px;
  color: #2c3e50;
}

.forecast-list {
  display: flex;
  overflow-x: auto;
  gap: 15px;
  padding-bottom: 10px;
}

.forecast-item {
  min-width: 150px;
  padding: 15px;
  background-color: #f9f9f9;
  border-radius: 6px;
  text-align: center;
}

.date {
  font-weight: bold;
  margin-bottom: 8px;
}

.weather-type {
  margin-bottom: 8px;
}

.temperature {
  color: #e91e63;
  margin-bottom: 8px;
}

.wind {
  font-size: 0.9rem;
  color: #666;
}

.update-time {
  margin-top: 20px;
  text-align: right;
  font-size: 0.8rem;
  color: #999;
}

.no-data {
  display: flex;
  justify-content: center;
  align-items: center;
  height: 200px;
  color: #999;
  font-size: 1.2rem;
}

.code-explanation {
  margin-top: 40px;
}

.explanation-item {
  margin-bottom: 30px;
}

.explanation-item h3 {
  margin-bottom: 15px;
  color: #2c3e50;
}

.code-example {
  background-color: #f8f8f8;
  border-radius: 6px;
  padding: 15px;
  overflow-x: auto;
}

pre {
  margin: 0;
}

code {
  font-family: 'Courier New', monospace;
  font-size: 0.9rem;
}

ul {
  padding-left: 20px;
}

li {
  margin-bottom: 8px;
}
</style>
